package xyz.riceball.generator.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Service;
import xyz.riceball.framework.core.enums.ENBool;
import xyz.riceball.framework.core.exception.RiceBallBizErrorException;
import xyz.riceball.framework.core.util.MybatisUtils;
import xyz.riceball.generator.common.enums.ENColumnKeyType;
import xyz.riceball.generator.common.enums.ENTemplateType;
import xyz.riceball.generator.entity.dto.GeneratorConfigDTO;
import xyz.riceball.generator.entity.po.ColumnPO;
import xyz.riceball.generator.entity.po.FieldMappingPO;
import xyz.riceball.generator.entity.po.TablePO;
import xyz.riceball.generator.entity.vo.CodeVO;
import xyz.riceball.generator.entity.vo.ColumnVO;
import xyz.riceball.generator.entity.vo.TableVO;
import xyz.riceball.generator.mapper.FieldMappingMapper;
import xyz.riceball.generator.mapper.GeneratorCodeMapper;
import xyz.riceball.generator.service.GeneratorCodeService;
import xyz.riceball.generator.utils.GeneratorUtils;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.StringWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * <p>
 *
 * </p>
 *
 * @author xiaovcloud
 * @since 2022/5/14 08:53
 */
@Service
public class GeneratorCodeServiceImpl implements GeneratorCodeService {

    @Resource
    private GeneratorCodeMapper generatorCodeMapper;
    @Resource
    private HttpServletResponse httpServletResponse;
    @Resource
    private FieldMappingMapper fieldMappingMapper;

    @Override
    public List<TablePO> queryList(Page<TablePO> page, TablePO tablePO) {
        return generatorCodeMapper.queryList(page, tablePO);
    }

    @Override
    public void generator(String tableNames) {
        List<String> tableNameList = StrUtil.split(tableNames, ',');
        Map<String, String> configMaps = MybatisUtils.getMap(
                "xyz.riceball.generator.mapper.GeneratorConfigMapper.selectMap",
                null, "configKey", "configValue");
        String dbType = MybatisUtils.getDbType();
        LambdaQueryWrapper<FieldMappingPO> wrapper = Wrappers.<FieldMappingPO>lambdaQuery();
        wrapper.eq(FieldMappingPO::getBdtype, dbType).eq(FieldMappingPO::getValid, ENBool.TRUE.getValue());
        List<FieldMappingPO> fieldMappingList = fieldMappingMapper.selectList(wrapper);

        List<CodeVO> codeVOList = new ArrayList<>();
        for (String tableName : tableNameList) {
            //设置tableVO的参数
            TablePO tablePO = generatorCodeMapper.queryTable(tableName);
            TableVO tableVO = new TableVO();
            BeanUtil.copyProperties(tablePO, tableVO);
            String tablePrefix = configMaps.get(GeneratorConfigDTO.TABLE_PREFIX);
            String classname = GeneratorUtils.removePrefixAndCamel(tableName, tablePrefix);
            String className = GeneratorUtils.capitalFirst(classname);
            tableVO.setClassName(className);
            tableVO.setClassname(classname);

            //设置字段的参数
            List<ColumnPO> columnPOList = generatorCodeMapper.queryColumn(tableName);
            List<ColumnVO> columnVOList = new ArrayList<>();
            for (ColumnPO columnPO : columnPOList) {
                ColumnVO columnVO = new ColumnVO();
                BeanUtil.copyProperties(columnPO, columnVO);
                if (ENColumnKeyType.PRI.getValue().equals(columnPO.getColumnKey())) {
                    tableVO.setPrimaryKey(columnVO);
                }
                String attrname = GeneratorUtils.underlineToCamel(columnPO.getColumnName());
                String attrName = GeneratorUtils.capitalFirst(attrname);
                //列名大写
                columnVO.setAttrName(attrName);
                //列名小写
                columnVO.setAttrname(attrname);
                //java类型
                String dataType = columnPO.getDataType();
                for (FieldMappingPO fieldMappingPO : fieldMappingList) {
                    if (fieldMappingPO.getSourceValue().equals(dataType)) {
                        try {
                            Class<?> clazz = Class.forName(fieldMappingPO.getTargetValue());
                            String clazzName = clazz.getName();
                            columnVO.setPackagePath(clazzName);
                            String clazzSimpleName = clazz.getSimpleName();
                            columnVO.setAttrType(clazzSimpleName);
                        } catch (ClassNotFoundException e) {
                            throw new RiceBallBizErrorException("数据字段映射异常:根据[{}]配置找不到Java类", fieldMappingPO.getTargetValue());
                        }
                    }
                }
                columnVOList.add(columnVO);
            }
            tableVO.setColumns(columnVOList);
            List<CodeVO> codeList = generatorCode(tableVO, columnVOList, configMaps);
            codeVOList.addAll(codeList);
        }
        codeToFile(codeVOList);
    }

    /**
     * 渲染模版
     *
     * @param table
     * @param columnList
     * @param configMaps
     * @return
     */
    private List<CodeVO> generatorCode(TableVO table, List<ColumnVO> columnList, Map<String, String> configMaps) {
        //设置velocity资源加载器
        Properties prop = new Properties();
        prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        Velocity.init(prop);
        //封装模板数据
        Map<String, Object> map = new HashMap<>();
        map.put("tableName", table.getTableName());
        map.put("comments", table.getComment());
        map.put("pk", table.getPrimaryKey());
        map.put("className", table.getClassName());
        map.put("classname", table.getClassname());
        map.put("columns", table.getColumns());
        String pkg = configMaps.get(GeneratorConfigDTO.PKG);
        map.put("pkg", pkg);
        String module = configMaps.get(GeneratorConfigDTO.MODULE);
        map.put("module", module);
        Set<String> pkgs = new HashSet<>();
        for (ColumnVO column : columnList) {
            if (!column.getPackagePath().startsWith("java.lang.")) {
                pkgs.add(column.getPackagePath());
            }
        }
        map.put("pkgs", pkgs);
        String author = configMaps.get(GeneratorConfigDTO.AUTHOR);
        map.put("author", author);
        LocalDateTime now = LocalDateTime.now();
        String createTime = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        map.put("createTime", createTime);

        VelocityContext context = new VelocityContext(map);
        //获取模板列表
        String fileTypes = configMaps.get(GeneratorConfigDTO.FILE_TYPES);

        List<ENTemplateType> templates = getTemplates();
        List<CodeVO> codeList = new ArrayList<>();

        for (ENTemplateType template : templates) {
            String templatePkg= template.getPkg();
            String templatePath = template.getTemplatePath();
            String suffix = template.getSuffix();

            //渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(templatePath, "UTF-8");
            tpl.merge(context, sw);

            CodeVO codeVO = new CodeVO();
            String fileName = table.getClassName() + GeneratorUtils.capitalFirst(suffix);
            codeVO.setFileName(fileName);
            String filePath = "." + File.separator + "generator" + File.separator + GeneratorUtils.package2FilePath(pkg)
                    + File.separator + templatePkg + File.separator + fileName;
            codeVO.setFilePath(filePath);
            codeVO.setContent(sw.toString());
            codeVO.setGenre("1");
            codeList.add(codeVO);
        }
        return codeList;
    }

    /**
     * 配置模版
     * @return
     */
    public static List<ENTemplateType> getTemplates() {
        List<ENTemplateType> templates = new ArrayList<>();
        //目前配置后端Java的配置。目前还不支持各种个性化配置，以后需要可再来思考
        templates.add(ENTemplateType.ENTITY);
        templates.add(ENTemplateType.MAPPER);
        templates.add(ENTemplateType.MAPPER_XML);
        templates.add(ENTemplateType.SERVICE);
        templates.add(ENTemplateType.SERVICE_IMPL);
        templates.add(ENTemplateType.CONTROLLER);
        return templates;
    }


    /**
     * 生成文件并下载
     *
     * @param codeVOList
     */
    private void codeToFile(List<CodeVO> codeVOList) {
        try {
            for (CodeVO codeVO : codeVOList) {
                File file = new File(codeVO.getFilePath());
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                if (!file.exists()) {
                    file.getParentFile().createNewFile();
                }
                FileOutputStream os = new FileOutputStream(file);
                os.write(codeVO.getContent().getBytes());
                os.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String filePath = "." + File.separator + "generator";
        File file = new File(filePath);
        File targetFile = new File("./generator.zip");

        try {
            File zipFile = ZipUtil.zip(targetFile, true, file);
            FileInputStream fileInputStream = new FileInputStream(zipFile);
            httpServletResponse.setContentType("application/x-download");
            httpServletResponse.setHeader("content-Disposition", "attachment;filename=generator.zip");
            ServletOutputStream outputStream = httpServletResponse.getOutputStream();
            IoUtil.copy(fileInputStream, outputStream);
            outputStream.flush();
        } catch (Exception e) {
            throw new RiceBallBizErrorException("写入文件错误", e);
        } finally {
            if (file.exists()) {
                FileUtil.del(file);
            }
            if (targetFile.exists()) {
                FileUtil.del(targetFile);
            }
        }

    }
}
